from math import atan,sin,cos,pi,sqrt
from gameobjects import GameObject,vel_limit
from common import Tick,BounderyVal
from pygame import Surface
#----------- Ammo types --------------------
class Shot(GameObject):
    create_sound = None
    explode_sound = None
    syncable = 0
    mass = 25
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=None, id=None,power=1.6,mass=None):
        GameObject.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y, self.image, id=id)
        self.power = power
        self.owner_id = owner_id
        self.alive_time = 0
        self.max_alive_time = 4 
        if mass: self.mass = mass
        self.sp = game_engine.soundplayer

    def move(self,dt):
        self.pos_x += self.vel_x*dt
        self.pos_y += self.vel_y*dt
        self.rect.center = (self.pos_x, self.pos_y)

    def explode(self):
        self.sp.play(self.explode_sound,0,0)

    def update(self,dt):
        self.alive_time += dt
        self.move(dt)

#        GameObject.update(self)

class ShotBomb(Shot):
    create_sound = None
    explode_sound = None
    mass = 80
    shreads_range = range(0,360,33)
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=None, id=None,power=5):
        Shot.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,power=power,owner_id=owner_id, id=id)
        self.max_alive_time = 2
        self.mass = 5
        
    def explode(self):
        vel = 400*self.game_engine.relative_speed
        rad_deg = pi/180
        # create a cluster of Shots
        ge = self.game_engine
        addShot = ge.addShot
        vx,vy = self.vel_x,self.vel_y
        px,py = self.pos_x,self.pos_y
        oid = self.owner_id
        for deg in self.shreads_range:
            dir_x,dir_y = (sin(deg*rad_deg), cos(deg *rad_deg) )
            addShot(Shot(ge,px,py,
                         vx+vel*dir_x, vy+vel*dir_y,
                         power=3,owner_id=oid),local=1,nosound=1)
        Shot.explode(self)

class ClusterBomb(Shot):
    create_sound = None
    explode_sound = None
    mass = 80
    shreads_range = range(0,360,33)
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=None, id=None,power=5):
        Shot.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,power=power,owner_id=owner_id, id=id)
        self.max_alive_time = 0.1
        self.mass = 5
        
    def explode(self):
        vel = 20*self.game_engine.relative_speed
        rad_deg = pi/180
        # create a cluster of Shots
        ge = self.game_engine
        addShot = ge.addShot
        vx,vy = self.vel_x,self.vel_y
        px,py = self.pos_x,self.pos_y
        oid = self.owner_id
        for deg in self.shreads_range:
            dir_x,dir_y = (sin(deg*rad_deg), cos(deg *rad_deg) )
            addShot(Shot(ge,px,py,
                         vx+vel*dir_x, vy+vel*dir_y,
                         power=3,owner_id=oid),local=1,nosound=1)
        Shot.explode(self)

class Mine(ShotBomb):
    syncable = 1
    mass = 150
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=None, id=None,power=15):
        ShotBomb.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=owner_id, id=id,power=power)
        self.mass = 50
        self.max_alive_time = 90
        self.shreads_range = range(0,360,16)
        self.exploded = 0

    def update(self,dt=None):
        self.decreaseVel(30.0*self.game_engine.relative_speed,dt)
        ShotBomb.update(self,dt)

    def explode(self):
        self.exploded = 1
        ShotBomb.explode(self)
        

class ProxyMine(ShotBomb):
    syncable = 1
    mass = 150
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=None, id=None,power=15):
        ShotBomb.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=owner_id, id=id,power=power)
        self.mass = 50
        self.max_alive_time = 90
        self.blinktick = Tick(0.3)

        self.armed = Tick(2)
        self.lock_radius = 50

        image = self.image.convert()
        image.fill((100,255,255))
        self.image1 = image.convert()
        image.fill((255,100,255))
        self.image2 = self.image.convert()
        self.exploded = 0

    def update(self,dt=None):
#        ShotBomb.update(self,dt)
        x,y = self.pos_x,self.pos_y
        self.decreaseVel(30.0*self.game_engine.relative_speed,dt)
        target = None
        if not self.armed.isSet():
            self.armed.tick(dt)
        else:
            self.blinktick.tick(dt)

            if self.blinktick.isSet():
                self.blinktick.reset()
                if self.image is self.image2:
                    self.image = self.image1
                else:
                    self.image = self.image2
            # explode if a ship is in range
            min_r = None
            for s in self.game_engine.vessels.sprites():
                if s.alive():
                    r = sqrt((x-s.pos_x)**2+(y-s.pos_y)**2)
                    if (min_r == None or r<min_r):
                        min_r=r
                        if self.lock_radius > r:
                            self.exploded = 1
                            return
        if dt:
            self.alive_time += dt
            self.move(dt)
        GameObject.update(self,dt)

    def explode(self):
        vel = 400
        rad_deg = pi/180
        # create a cluster of Shots
        ge = self.game_engine
        addShot = ge.addShot
        vx,vy = self.vel_x,self.vel_y
        px,py = self.pos_x,self.pos_y
        oid = self.owner_id
        for deg in range(0,360,33):
            dir_x,dir_y = (sin(deg*rad_deg), cos(deg *rad_deg) )
            addShot(Shot(ge,px,py,
                         vx+vel*dir_x, vy+vel*dir_y,
                         power=8,owner_id=oid,mass=200),local=1,nosound=1)
        
        self.exploded = 1
        Shot.explode(self)
        

class Missile(ShotBomb):
    syncable = 1
    mass = 200
    images = None
    armed_image = None
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=None, id=None,power=10):
        ShotBomb.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,owner_id=owner_id, id=id,power=power)
        self.max_alive_time = 10
        self.armed = Tick(1)
        self.max_vel = 350*game_engine.relative_speed
        self.v_change = 1100*game_engine.relative_speed
        self.lock_radius = 200*game_engine.relative_speed
        self.target = None
        if not self.images:
            self._calcImages(self.image)
        self.image_nr = 0
        self.dir = 1
        self.exploded = 0
        
    def _calcImages(self,image):
        ' create the images array with pulsating images and the armed image'
        images = []
        image = Surface(self.image.get_size())
        # create pulsating image array
        for i in range(64,255,16):
            image.fill((255,i,0))
            images.append(image.convert())
        self.__class__.images = images
        # create armed image
        image.fill((255,200,0))
        self.__class__.armed_image = image.convert()
        self.__class__.image = image.convert()
        
    def update(self,dt=None):
        x,y = self.pos_x,self.pos_y
        target = self.target
        i_nr = self.image_nr
        if not self.armed.isSet():
            self.armed.tick(dt)
        else:
            # lock on nearest, inreach and alive target
            min_r = None
            for s in self.game_engine.vessels.sprites():
                if s.alive():
                    r = sqrt((x-s.pos_x)**2+(y-s.pos_y)**2)
                    if (min_r == None or r<min_r):
                        min_r=r
                        if self.lock_radius > r:
                            target = s

            if target and target.alive():
                # change the image, to get it pulsating
                i_nr+=dt*32*self.dir
                if i_nr >= len(self.images)-1:
                    i_nr =len(self.images)-1
                    self.dir = -1
                elif i_nr < 1:
                    i_nr = 1
                    self.dir = 1

                self.image= self.images[int(i_nr)]
                # persue the target
                s = target
                vd = self.v_change
                dx = -(x-s.pos_x)/min_r
                dy = -(y-s.pos_y)/min_r
                self.changeVel(dx*vd,dy*vd,dt)
                self.velLimit()
            else:
                target = None
                self.image = self.armed_image
                # if missile armed and has no lock, slow down
                self.decreaseVel(100,dt)
    
        self.image_nr = i_nr
        self.target = target
        ShotBomb.update(self,dt)

    def velLimit(self):
        ' limit velosity to max_vel '
        self.vel_x,self.vel_y = vel_limit(self.vel_x,self.vel_y,self.max_vel)


#----------- Weapons --------------------
class Weapon:
    def __init__(self,owner,fire_interval,ammo=50,max_ammo=None):
        self.owner = owner
        self.fire_inverval = Tick(fire_interval)
        if not max_ammo: max_ammo=ammo
        self.max_ammo = float(max_ammo)
        self.ammo = BounderyVal(0,ammo,ammo)
        self.fire_inverval.set()
        self.Shot = Shot

    def fire(self,ammo=1):
        ' dec ammo and returns true if ammo left'
        if self.fire_inverval.isSet():
            self.fire_inverval.reset()
            if self.ammo.val > 0 :
                self.ammo.change(-ammo)
                return 1
        return 0
    def update(self,dt):
        self.fire_inverval.tick(dt)
        if self.ammo.max > self.max_ammo:
            self.ammo.max = self.max_ammo

        self.mass = self.ammo.val*self.Shot.mass

class Gun(Weapon):
    def __init__(self,owner,fire_interval=0.07,ammo=150,max_ammo=300):
        Weapon.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 300*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = Shot
    def fire(self):
        if Weapon.fire(self):
            ship = self.owner.ship
            ship_rect = ship.rect
            ang = ship.ang
            shot_vel = self.shot_vel
            r = sqrt((ship_rect.width/2)**2+(ship_rect.height/2)**2)+1

            pos_x = ship_rect.center[0]+r*-sin(ang)
            pos_y = ship_rect.center[1]+r*-cos(ang)
            vel_x = shot_vel*-sin(ang)+self.owner.ship.vel_x
            vel_y = shot_vel*-cos(ang)+self.owner.ship.vel_y

            shot = self.Shot(self.game_engine, pos_x, pos_y, vel_x, vel_y, owner_id=self.owner.id)
            self.game_engine.addShot(shot)

class Bomber(Gun):
    def __init__(self,owner,fire_interval=0.5,ammo=10,max_ammo=20):
        Gun.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 300*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = ShotBomb
class ClusterBomber(Gun):
    def __init__(self,owner,fire_interval=0.5,ammo=10,max_ammo=20):
        Gun.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 300*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = ClusterBomb

class Launcher(Gun):
    def __init__(self,owner,fire_interval=2,ammo=3,max_ammo=5):
        Gun.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 185*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = Missile

class TriGun(Weapon):
    def __init__(self,owner,fire_interval=0.20,ammo=100,max_ammo=200):
        Weapon.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 300*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = Shot
    def fire(self):
        if Weapon.fire(self,3):
            ship = self.owner.ship
            ship_rect = ship.rect
            ang = ship.ang
            d = 5 # distance between shots
            shot_vel = self.shot_vel
            r = sqrt((ship_rect.width/2)**2+(ship_rect.height/2)**2)+1

            pos_x = ship_rect.center[0]+r*-sin(ang)
            pos_y = ship_rect.center[1]+r*-cos(ang)
            vel_x = shot_vel*-sin(ang)+ship.vel_x
            vel_y = shot_vel*-cos(ang)+ship.vel_y

            pos_x1 = pos_x+d*-sin(ang)
            pos_y1 = pos_y+d*-cos(ang)
            pos_x2 = pos_x+d*sin(ang-pi/2)
            pos_y2 = pos_y+d*-cos(ang+pi/2)
            pos_x3 = pos_x-d*sin(ang-pi/2)
            pos_y3 = pos_y-d*-cos(ang+pi/2)
            addShot = self.game_engine.addShot
            shot = self.Shot(self.game_engine, pos_x1, pos_y1, vel_x, vel_y, owner_id=self.owner.id)
            addShot(shot)
            shot = self.Shot(self.game_engine, pos_x2, pos_y2, vel_x, vel_y, owner_id=self.owner.id)
            addShot(shot,nosound=1)
            shot = self.Shot(self.game_engine, pos_x3, pos_y3, vel_x, vel_y, owner_id=self.owner.id)
            addShot(shot,nosound=1)

class TwinGun(Weapon):
    def __init__(self,owner,fire_interval=0.07,ammo=100,max_ammo=200):
        Weapon.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 300*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = Shot
        self.gunnr = 0
    def fire(self):
        if Weapon.fire(self):
            ship = self.owner.ship
            ship_rect = ship.rect
            ang = ship.ang
            d = 5 # distance between shots
            shot_vel = self.shot_vel
            r = sqrt((ship_rect.width/2)**2+(ship_rect.height/2)**2)+1

            pos_x = ship_rect.center[0]+r*-sin(ang)
            pos_y = ship_rect.center[1]+r*-cos(ang)
            vel_x = shot_vel*-sin(ang)+ship.vel_x
            vel_y = shot_vel*-cos(ang)+ship.vel_y

            if self.gunnr:
                pos_x = pos_x+d*sin(ang-pi/2)
                pos_y = pos_y+d*-cos(ang+pi/2)
                self.gunnr = 0
            else:
                pos_x = pos_x-d*sin(ang-pi/2)
                pos_y = pos_y-d*-cos(ang+pi/2)
                self.gunnr = 1
            addShot = self.game_engine.addShot
            shot = self.Shot(self.game_engine, pos_x, pos_y, vel_x, vel_y, owner_id=self.owner.id)
            self.game_engine.addShot(shot)

class SweapGun(Weapon):
    def __init__(self,owner,fire_interval=0.07,ammo=100,max_ammo=200):
        Weapon.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 300*owner.game_engine.relative_speed
        self.sweap_d = pi/2
        self.max_sweep = (pi/180)*8
        self.sweap = 0
        self.game_engine = owner.game_engine
        self.Shot = Shot

    def fire(self):
        if Weapon.fire(self):
            ship = self.owner.ship
            ship_rect = ship.rect
            ang = ship.ang
            shot_vel =self.shot_vel
            r = sqrt((ship_rect.width/2)**2+(ship_rect.height/2)**2)+1

            pos_x = ship_rect.center[0]+r*-sin(ang)
            pos_y = ship_rect.center[1]+r*-cos(ang)
            vel_x = shot_vel*-sin(self.owner.ship.ang+self.sweap)+ship.vel_x
            vel_y = shot_vel*-cos(self.owner.ship.ang+self.sweap)+ship.vel_y

            shot = self.Shot(self.game_engine, pos_x, pos_y, vel_x, vel_y, owner_id=self.owner.id)
            self.game_engine.addShot(shot)

    def update(self,dt=None):
        Weapon.update(self,dt)
        # change dir of sweep
        if (self.sweap > self.max_sweep and self.sweap_d > 0) or (self.sweap < -self.max_sweep and self.sweap_d < 0):
            self.sweap_d = -self.sweap_d
        self.sweap += self.sweap_d*dt            


class MineLayer(Weapon):
    def __init__(self,owner,fire_interval=1,ammo=5,max_ammo=10):
        Weapon.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.shot_vel = 50*owner.game_engine.relative_speed
        self.game_engine = owner.game_engine
        self.Shot = Mine

    def fire(self):
        if Weapon.fire(self):
            ship = self.owner.ship
            ship_rect = ship.rect
            ang = ship.ang
            shot_vel =self.shot_vel
            r = sqrt((ship_rect.width/2)**2+(ship_rect.height/2)**2)+3

            pos_x = ship_rect.center[0]-r*-sin(ang)
            pos_y = ship_rect.center[1]-r*-cos(ang)

            vel_x = -shot_vel*-sin(ang)+ship.vel_x
            vel_y = -shot_vel*-cos(ang)+ship.vel_y
            shot = self.Shot(self.game_engine, pos_x, pos_y, vel_x, vel_y, owner_id=self.owner.id)
            self.game_engine.addShot(shot)

class ProxyMineLayer(MineLayer):
    def __init__(self,owner,fire_interval=0.8,ammo=10,max_ammo=15):
        MineLayer.__init__(self,owner,fire_interval,ammo,max_ammo)
        self.Shot = ProxyMine
